home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pp / pp-6.0 / Chans / fax / dexNet200 / dexnet_util.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-18  |  18.4 KB  |  790 lines

  1. /* dexnet_util.c: various utility routines */
  2.  
  3. # ifndef lint
  4. static char Rcsid[] = "@(#)$Header: /xtel/pp/pp-beta/Chans/fax/dexNet200/RCS/dexnet_util.c,v 6.0 1991/12/18 20:08:53 jpo Rel $";
  5. # endif
  6.  
  7. /*
  8.  * $Header: /xtel/pp/pp-beta/Chans/fax/dexNet200/RCS/dexnet_util.c,v 6.0 1991/12/18 20:08:53 jpo Rel $
  9.  *
  10.  * $Log: dexnet_util.c,v $
  11.  * Revision 6.0  1991/12/18  20:08:53  jpo
  12.  * Release 6.0
  13.  *
  14.  */
  15.  
  16. /* Modified by Pekka Nikander for sunos 4 */
  17.  
  18. #include <sys/types.h>
  19. #include <sys/ioctl.h>
  20. #include <sys/stat.h>
  21. #include <termio.h>
  22. #include <time.h>
  23. #include <stdio.h>
  24. #include <fcntl.h>
  25. #include <setjmp.h>
  26. #include <sgtty.h>
  27. #include <signal.h>
  28. #include <errno.h>
  29. #include "dexnet.h"
  30.  
  31. /* pp includes */
  32. #include    "util.h"
  33. #include    "retcode.h"
  34. #include    "adr.h"
  35. #include    "qmgr.h"
  36. #include     <isode/cmd_srch.h>
  37. #include     "tb_bpt88.h"
  38. #include    "IOB-types.h"
  39. #include    "MTA-types.h"
  40. #include    "or.h"
  41. #include     <varargs.h>
  42. #ifndef sun
  43. #include    <sys/termios.h>
  44. #endif
  45. #include    "../faxgeneric.h"
  46.  
  47. /*
  48.  *    Driver for Fujitsu dexNET 200 fax modem, adopted for DECstation 
  49.  *    5000. 
  50.  *
  51.  *    This driver is based on a driver written by Jim Knowles (NASA). Many
  52.  *    thanks go Jim for his help.
  53.  *
  54.  *    This driver assumes that hardware (CTS) flow control is in use.
  55.  *    At one point, I attempted to make it work with XON/XOFF. However,
  56.  *    I never could make this work well. The problem was that there
  57.  *    was a non-zero delay between the time that the dexnet buffer was
  58.  *    full and the time that the XOFF was received by this program. 
  59.  *    Invariably, the program would write a few hundred bytes more after
  60.  *    the buffer was full before getting the XOFF. This would corrupt
  61.  *    the buffer resulting in either a loss of a few scan lines or
  62.  *    a complete garbage page.
  63.  *
  64.  *    Note that when the dexnet does a reset, DSR fluctuates, so this pin
  65.  *    must be tied high for the kernel to be happy. Also, DCD fluctates 
  66.  *    in response to the fax telephone handshake; it must also be tied
  67.  *    high for the device driver to be happy.
  68.  *
  69.  *    Note that the error processing is complicated because of the
  70.  *    fact that the entire g3 image may be written to the modem
  71.  *    before any response is received (if the image is short
  72.  *    enough). In this case, the errors (like busy) must be
  73.  *    processed by the end of message procedure.
  74.  *
  75.  *    This driver was developed for a DECstation 5000. The cable used to
  76.  *    connect the dexnet to the 5000 was rs232:
  77.  *
  78.  * Modem (Male)         Dec (Female)
  79.  *        TD 2-------------2
  80.  *        RD 3-------------3
  81.  *       CTS 5-------------5
  82.  *       GND 7-------------7
  83.  *       +12 9-----+-------6 DSR
  84.  *                 |-------8 DCD
  85.  *      DTR 20-------------20
  86.  *
  87.  *    Information about the dexNet 200 can be obtained from
  88.  *
  89.  *            Fujitsu
  90.  *            Imaging Systems of America, Inc
  91.  *            Danbury Connecticut, 06810
  92.  *            (800) 537-1262
  93.  *
  94.  *    Robert Hagens
  95.  *    November, 1991
  96.  */
  97. int        total_cc = 0;
  98. static     int     readDebug = 0;
  99. extern int    fax_debug;
  100.  
  101. #define doIoctl(dem, ioctlName, arg, msg)\
  102.     if (ioctl((dem)->fd, (ioctlName), (arg)) == -1) {\
  103.         sprintf((dem)->errBuf, "ioctl error: %s: %s", msg, sys_errname(errno));\
  104.         return(NOTOK);\
  105.     }
  106.  
  107. /*
  108.  *  dexNET 200 error response
  109.  */
  110. struct {
  111.     int        code;
  112.     char    *text;
  113. } responseCodes[] = {
  114.     dexUnknown, "Unknown response",
  115.     dexNotReady, "Not Ready",
  116.     dexReady,     "Ready",
  117.     dexConnected, "Connected",
  118.     dexWaiting,    "Waiting for call",
  119.     dexDialing,    "Dialing",
  120.     dexSID,        "Station Identification",
  121.     dexTID,        "Terminal Identification",
  122.     dexStandardRes, "Standard Resolution",
  123.     dexFineRes,    "Fine Resolution",
  124.     dexCallTerm,    "Call terminated normally",
  125.     dexAck,        "Ack",
  126.     dexNak,        "Nak",
  127.     dexRing,    "Ring",
  128.     dexBufferLow,"Buffer low",
  129.     dex9600Baud,    "Line speed 9600",
  130.     dex7200Baud,    "Line speed 7200",
  131.     dex4800Baud,    "Line speed 4800",
  132.     dex2400Baud,    "Line speed 2400",
  133.     dexScanError,    "Scan line error",
  134.     dexVersion,        "Version",
  135.     dexBusy,        "Busy",
  136.     dexNoResponse,    "No response",
  137.     dexNotAFax,        "Not a Fax",
  138.     dexDisconnect,    "Line Disconnect",
  139.     dexResError,    "Reserved Error",
  140.     dexUnderrun,    "Buffer Underrun",
  141.     dexBufFull,        "Buffer Full",
  142.     dexBufEmpty,    "Buffer Empty",
  143.     -1, ""
  144. };
  145.  
  146. /* map an response code to a textual string */
  147. static char *
  148. codeToString(code)
  149. int    code;
  150. {
  151.     int i;
  152.     for (i=0; responseCodes[i].code != -1; i++) {
  153.         if (responseCodes[i].code == code)
  154.             return(responseCodes[i].text);
  155.     }
  156.     return("Code Unknown");
  157. }
  158.  
  159. /*
  160.  *    Open device. Configure line to device. Return ptr to
  161.  *    modem control block
  162.  */
  163. openDexModem(dem)
  164. DexModem    *dem;
  165. {
  166.     struct sgttyb    ttyb;                /* structure for tty parameters */
  167.     int                disc = NTTYDISC;    /* line    discipline */
  168.     int                lflags = LLITOUT;    /* local flags */
  169.     int                temp = 0;
  170.  
  171.     if ((dem->fd = open(dem->devName, O_RDWR)) == -1) {
  172.         sprintf(dem->errBuf, "%s: open: %s", dem->devName, 
  173.             sys_errname(errno));
  174.         return(NOTOK);
  175.     }
  176.  
  177.     /* Set the line discipline */
  178.     doIoctl(dem, TIOCSETD, (char *)&disc, "TIOCSETD");
  179.  
  180. #ifdef TIOCMODEM
  181.     /* Set this tty to be a modem */
  182.        doIoctl(dem, TIOCMODEM, (char *)&temp, "TIOCMODEM");
  183. #endif
  184.  
  185.     /* Hang up the fax - When a close is done on pDevName */
  186.     doIoctl(dem, TIOCHPCL, (char *)NULL, "TIOCHPCL");
  187.  
  188.     /* Fetch the basic parameters */
  189.     doIoctl(dem, TIOCGETP, (char *)&ttyb, "TIOCGETP");
  190.  
  191.     /* Set the speed to 9600 */
  192.     ttyb.sg_ispeed = ttyb.sg_ospeed = B9600;
  193.     ttyb.sg_flags = ANYP| RAW | PASS8;
  194.     dem->highSpeed = 0;
  195.  
  196.     /* Set these parameters */
  197.     doIoctl(dem, TIOCSETP, (char *)&ttyb, "TIOCSETP");
  198.  
  199.     /* Suppress output translations */
  200.     doIoctl(dem, TIOCLSET, (char *)&lflags, "TIOCLSET");
  201.  
  202.     return(OK);
  203. }
  204.  
  205. /* make fd non blocking I/O */
  206. static
  207. nonBlockIO(fd)
  208. int    fd;
  209. {
  210.     int    flags;
  211.     fcntl(fd, F_GETFL, &flags);
  212.     flags |= O_NDELAY;
  213.     fcntl(fd, F_SETFL, &flags);
  214. }
  215.  
  216. static
  217. blockIO(fd)
  218. int    fd;
  219. {
  220.     int    flags;
  221.     fcntl(fd, F_GETFL, &flags);
  222.     flags &= ~O_NDELAY;
  223.     fcntl(fd, F_SETFL, &flags);
  224. }
  225.  
  226. /*
  227.  *    Send command to modem. Make sure 'len' bytes were written.
  228.  *    Msg is used only for diagnostics. Return success or failure of op
  229.  */
  230. writeDexModem(dexp, buf, len, msg)
  231. DexModem    *dexp;
  232. unsigned char    *buf;
  233. int        len;
  234. char    *msg;
  235. {
  236.     int cc;
  237. #ifdef    DUMPWRITE
  238.      if (len < 100) {
  239.         int i;
  240.         printf("writeDexModem: ");
  241.         for (i=0; i<len; i++)
  242.             printf("\\%03o", *(buf+i));
  243.         printf("\n");
  244.     }
  245. #endif    DUMPWRITE
  246.  
  247.     total_cc += len;
  248.     while ((cc = write(dexp->fd, buf, len)) == -1 && 
  249.            (errno == EWOULDBLOCK || errno == EAGAIN)) {
  250.         fd_set writefds;
  251.  
  252.         FD_ZERO(&writefds);
  253.         FD_SET(dexp->fd, &writefds);
  254.  
  255.         if (select(dexp->fd+1, NULL, &writefds, NULL, NULL) < 0) {
  256.             sprintf(dexp->errBuf, "select: %s", sys_errname(errno));
  257.             return(NOTOK);
  258.         }
  259.     }
  260.     if (cc != len) {
  261.         sprintf(dexp->errBuf, "write: %s", sys_errname(errno));
  262.         return(NOTOK);
  263.     }
  264.     return(OK);
  265. }
  266.  
  267. /* block/read one char from the modem. fd may be in non-blocking mode, so
  268.  use select to block here if we get EWOULDBLOCK */
  269. static unsigned char
  270. readModem(dexp)
  271. DexModem    *dexp;
  272. {
  273.     int                l;
  274.     unsigned char    c = NULL;
  275.  
  276.     if ((l = read(dexp->fd, &c, 1)) < 0) {
  277.         if (errno == EWOULDBLOCK || errno == EAGAIN) {
  278.             fd_set            readfds;
  279.             FD_ZERO(&readfds);
  280.             FD_SET(dexp->fd, &readfds);
  281.  
  282.             if (select(dexp->fd+1, &readfds, NULL, NULL, NULL) < 0) {
  283.                 perror("select");
  284.             }
  285.             /* could assert (FD_ISSET(dexp->fd, &readfds)) at this point */
  286.             if ((l = read(dexp->fd, &c, 1)) < 0)
  287.                 perror("readModem");
  288.         } else
  289.             perror("readModem");
  290.     }
  291.  
  292.     if (readDebug)
  293.         PP_TRACE(("readModem: x%02x\n", c));
  294.     return(c);
  295. }
  296.  
  297. /* Return true if there is data ready to be read on modem */
  298. static char
  299. dataPresent(dexp)
  300. DexModem    *dexp;
  301. {
  302.     long    cc;
  303.     if (ioctl(dexp->fd, FIONREAD, &cc) == -1) {
  304.         perror("ioctl: FIONREAD");
  305.         return(0);
  306.     }
  307.     return(cc > 0);
  308. }
  309.  
  310. /* 
  311.  *    Parse the response contained in buf and return an integer code
  312.  *    corresponding to the response. Some responses contain an ascii
  313.  *    string as part of the response. This is returned in misc
  314.  */
  315. static
  316. readResponse(dexp, misc)
  317. DexModem    *dexp;
  318. char        **misc;    /* ascii part of response (assume misc is big enough) */
  319. {
  320.     static unsigned char    buf[100], *bp;
  321.     int    code = dexUnknown;
  322.     unsigned char    c;
  323.  
  324.     /* TODO: set timeout to catch hanging read */
  325.  
  326.     bzero(buf, sizeof(buf));
  327.     bp = buf;
  328.  
  329.     c = readModem(dexp);
  330.     if (c == ESC) {
  331.         switch(readModem(dexp)) {
  332.             case DLE:
  333.                 switch(readModem(dexp)) {
  334.                     case '0': code = dexNotReady; break;
  335.                     case '1': code = dexReady; break;
  336.                     case '2': code = dexConnected; break;
  337.                     case '3': code = dexDisconnect; break;
  338.                     case '4': code = dexWaiting; break;
  339.                     case '5': code = dexNotAFax; break;
  340.                     case '6': code = dexDialing; break;
  341.                     case '7': code = dexBusy; break;
  342.                     case '8': code = dexNoResponse; break;
  343.                     case '9':
  344.                         code = dexSID;
  345.                         while ((c = readModem(dexp)) != CR)
  346.                             *bp++ = c;
  347.                         break;
  348.                     case ':':
  349.                         code = dexTID;
  350.                         while ((c = readModem(dexp)) != CR)
  351.                             *bp++ = c;
  352.                         break;
  353.                     case '=': code = dexCallTerm; break;
  354.                     case 'R': code = dexRing; break;
  355.                     case 'W': code = dexBufferLow; break;
  356.                     case ';': code = dexResError; break;
  357.                     case '<': code = dexUnderrun; break;
  358.                     default:
  359.                         break;
  360.                 }
  361.                 break;
  362.             
  363.             case BEL:
  364.                 switch(readModem(dexp)) {
  365.                     case '0': code = dexStandardRes; break;
  366.                     case '1': code = dexFineRes; break;
  367.                     case 'R':
  368.                         *bp++ = 'R';
  369.                         while ((c = readModem(dexp)) != CR)
  370.                             *bp++ = c;
  371.                         code = dexVersion;
  372.                         break;
  373.                     default:
  374.                         break;
  375.                 }
  376.                 break;
  377.             case FS:
  378.                 switch(readModem(dexp)) {
  379.                     case '\'': code = dex9600Baud; break;
  380.                     case 'H': code = dex7200Baud; break;
  381.                     case '0': code = dex4800Baud; break;
  382.                     case CAN: code = dex2400Baud; break;
  383.                     case '1': code = dexFineRes; break;
  384.                     case 'R':
  385.                         *bp++ = 'R';
  386.                         while ((c = readModem(dexp)) != CR)
  387.                             *bp++ = c;
  388.                         code = dexVersion;
  389.                         break;
  390.                     default:
  391.                         break;
  392.                 }
  393.             case HT:
  394.                 readModem(dexp);    /* returns # of scan lines in err */
  395.                 code = dexScanError;
  396.                 break;
  397.             case ACK:
  398.                 code = dexAck;
  399.                 *bp++ = readModem(dexp);
  400.                 break;
  401.  
  402.             case NAK:
  403.                 code = dexNak;
  404.                 *bp++ = readModem(dexp);
  405.                 break;
  406.         }
  407.     } else if (c == XXON) {
  408.         code = dexBufEmpty;
  409.     } else if (c == XXOFF) {
  410.         code = dexBufFull;
  411.     }
  412.  
  413.     if ((misc != NULL) && (bp != buf))
  414.         *misc = (char *)buf;
  415.  
  416.     PP_LOG(LLOG_DEBUG, 
  417.        ("readResponse: %d: %s (%s)", code, codeToString(code), buf));
  418.  
  419.     return(code);
  420. }
  421.  
  422. /* 
  423.  *    physically reset dexnet by dropping DTR for 4 seconds.
  424.  *    note: this is a *real time* constraint. DTR must be dropped
  425.  *    for at least 3.1 seconds but not more than 5.9 seconds. Thank
  426.  *    you very much, fujitsu.
  427.  */
  428. hardResetDexModem(dem)
  429. DexModem *dem;
  430. {
  431.    ioctl(dem->fd, TIOCSDTR, 0);
  432.    ioctl(dem->fd, TIOCCDTR, 0);
  433.    sleep(4);
  434.    ioctl(dem->fd, TIOCSDTR, 0);
  435.  
  436.    /* we don't yet know if this worked, but if it did, the speed is now 
  437.     * 9600. If it didn't, we're screwed anyway.
  438.     */
  439.    dem->highSpeed = 0;
  440. }
  441.  
  442. char nulls[] = {'\000', '\000'};
  443. /*
  444.  *    Set up the modem. Return OK or NOTOK
  445.  *    This can be called no matter what state the modem
  446.  *    is in.
  447.  */
  448. setupDexModem(dem, fast)
  449. DexModem *dem;
  450. int        fast;    /* true if 19.2K baud */
  451. {
  452. #ifdef sun
  453.     struct termios    tty_struct;
  454. #endif
  455.     struct sgttyb    ttyb;
  456.     char            *p;
  457.  
  458.     fax_debug = 1;
  459.  
  460.     /*
  461.      *    This should bring the modem back to 9600 baud, text mode
  462.      */
  463.     hardResetDexModem(dem);
  464.  
  465.     /*
  466.      *    Make sure tty is at 9600 baud
  467.      */
  468.     doIoctl(dem, TIOCGETP, (char *)&ttyb, "TIOCGETP");
  469.     ttyb.sg_ispeed = ttyb.sg_ospeed = B9600;
  470.     doIoctl(dem, TIOCSETP, (char *)&ttyb, "TIOCSETP");
  471.  
  472. #ifdef sun
  473.     /* 
  474.      * Set up hardware flow control 
  475.      */
  476.     doIoctl(dem, TCGETS, (char *)&tty_struct, "TCGETS");
  477.     tty_struct.c_cflag |= CRTSCTS;
  478.     tty_struct.c_cflag |= CLOCAL;
  479.     doIoctl(dem, TCSETS, (char *)&tty_struct, "TCSETS");
  480. #endif
  481.  
  482.     /*
  483.      *    Return to factory defaults
  484.      */
  485.     if (writeDexModem(dem, FACTDEF, sizeof(FACTDEF)-1, "FACTDEF:") == NOTOK) {
  486.         PP_LOG(LLOG_EXCEPTIONS, (dem->errBuf));
  487.         return(NOTOK);
  488.     }
  489.     if (readResponse(dem, NULL) != dexReady) {
  490.         strcat(dem->errBuf, "Modem not ready (FACTDEF)");
  491.         return(NOTOK);
  492.     }
  493.  
  494.     /* soft reset as well */
  495.     IssueCommand(dem, RESET, "RESET");
  496.     if (readResponse(dem, NULL) != dexReady) {
  497.         strcat(dem->errBuf, "Modem not ready (RESET)");
  498.         return(NOTOK);
  499.     }
  500.  
  501.     IssueCommand(dem, ASK_VERSION, "Check Version");
  502.     readResponse(dem, &p);
  503.     if ((p == NULL) || ((strcmp(p, "REV1.9") != 0) &&
  504.                 (strcmp(p, "REV2.0") != 0))) {
  505.         strcat(dem->errBuf, "Modem software version mismatch: expect rev1.9 or rev2.0");
  506.         return(NOTOK);
  507.     }
  508.  
  509.     IssueCommand(dem, SPEAKER_ON, "Set Speaker On");
  510.  
  511.     if (fast) {
  512.         /* Send at 19200 */
  513.         dem->highSpeed = 1;
  514.         IssueCommand(dem, BAUD_19200, "Set baud 19200");
  515.  
  516.         /* Set the speed to 19200 */
  517.         doIoctl(dem, TIOCGETP, (char *)&ttyb, "TIOCGETP");
  518.         ttyb.sg_ispeed = ttyb.sg_ospeed = B19200;
  519.         doIoctl(dem, TIOCSETP, (char *)&ttyb, "TIOCSETP");
  520.     } else {
  521.         dem->highSpeed = 0;
  522.         IssueCommand(dem, BAUD_9600, "Set baud 9600");
  523.     }
  524.  
  525.     /*
  526.      *    WARNING: response code parse routines now assume zero retry
  527.      *    will be set. This parameter changes the order and kind of
  528.      *    responses that the dexnet will generate
  529.      */
  530.       IssueCommand(dem, ZERO_RETRY, "Set ZERO_RETRY");
  531.  
  532.     /* make sure modem is ready */
  533.     IssueCommand(dem, ASK_STAT, "Check Status");
  534.     if (readResponse(dem, NULL) == dexReady)
  535.         return(OK);
  536.     else {
  537.         /* try reset again (needed when running at 19200 */
  538.         IssueCommand(dem, RESET, "RESET");
  539.         if (readResponse(dem, NULL) == dexReady) {
  540.             IssueCommand(dem, ZERO_RETRY, "Set ZERO_RETRY");
  541.             return(OK);
  542.         } else {
  543.             strcat(dem->errBuf, "Modem not ready");
  544.             return(NOTOK);
  545.         }
  546.     }
  547.  
  548. }
  549.  
  550.  
  551. /* This function will dial a number, set the date, time and
  552.     TTI strings on the page header.  A OK return signifies
  553.     a successful completion.  A null number will cause this
  554.     routine to return a NOTOK.  The date, time and TTI are
  555.     optional.  To not send these, just pass in NULLs.
  556. */
  557. dialDexModem(dem, snumber, sdate, stime, stti)
  558. DexModem    *dem;
  559. char *snumber, *sdate, *stime, *stti;
  560. {
  561.     /* dial number */
  562.     if (snumber == NULL) {
  563.         strcat(dem->errBuf, "can't dial NULL number");
  564.         return(NOTOK);
  565.     }
  566.  
  567.     IssueCommand(dem, DIAL_NMBR, "Dial number");
  568.     IssueData(dem, snumber, strlen(snumber));
  569.     IssueData(dem, "\r", 1);
  570.  
  571.     /* Send date if not NULL */
  572.     if (sdate) {
  573.         IssueCommand(dem, SEND_DATE, "Send date");
  574.         IssueData(dem, sdate, strlen(sdate));
  575.         IssueData(dem, "\r", 1);
  576.     }
  577.  
  578.     /* Send the time if not NULL */
  579.     if (stime) {
  580.         IssueCommand(dem, SEND_TIME, "Send time");
  581.         IssueData(dem, stime, strlen(stime));
  582.         IssueData(dem, "\r", 1);
  583.     }
  584.  
  585.     /* Send the TTI if not NULL */
  586.     if (stti) {
  587.         IssueCommand(dem, SEND_TTI, "Send TTI");
  588.         IssueData(dem, stti, strlen(stti));
  589.         IssueData(dem, "\r", 1);
  590.     }
  591.     return(OK);
  592. }
  593.  
  594. /*
  595.  *    Interpret response code; possible set error message in msgBuf.
  596.  *    return OK or NOTOK depending on code received. It is assumed that
  597.  *    if NOTOK is returned, a message will be in msgBuf. 
  598.  */
  599. static
  600. processResponse(dem, code, buf)
  601. DexModem    *dem;
  602. int            code;
  603. char        *buf;
  604. {
  605.     int    retcode = OK;
  606.     if (buf == NULL)
  607.         buf = "";
  608.         
  609.     dem->msgCode = dexReady; /* no error */
  610.  
  611.     switch (code) {
  612.         case dexAck: 
  613.             PP_TRACE(("Page %s sent ok", buf));
  614.             break;
  615.         case dexNak: 
  616.             PP_TRACE(("Page %s sent with errors", buf));
  617.             dem->xmitErrs++; 
  618.             break;
  619.         case dexConnected:
  620.             PP_TRACE(("Connected"));
  621.             if (dem->stationId[0])
  622.                 PP_TRACE(("\tto station %s", dem->stationId));
  623.             if (dem->terminalId[0])
  624.                 PP_TRACE(("\tto terminal %s", dem->terminalId));
  625.             break;
  626.  
  627.         case dexNoResponse:
  628.             sprintf(dem->msgBuf, "No response from remote fax");
  629.             PP_TRACE(("%s", dem->msgBuf));
  630.             dem->msgCode = dexNoResponse;
  631.             retcode = NOTOK;
  632.             break;
  633.             
  634.         case dexBusy:
  635.             sprintf(dem->msgBuf, "Remote fax line is busy");
  636.             PP_TRACE(("%s", dem->msgBuf));
  637.             dem->msgCode = dexBusy;
  638.             retcode = NOTOK;
  639.             break;
  640.  
  641.         case dexSID:
  642.             strcpy(dem->stationId, buf);
  643.             break;
  644.  
  645.         case dexTID:
  646.             strcpy(dem->terminalId, buf);
  647.             break;
  648.  
  649.         case dexNotAFax:
  650.             /* fatal message */
  651.             sprintf(dem->msgBuf, "Remote device is not a fax");
  652.             PP_TRACE(("%s", dem->msgBuf));
  653.             retcode = NOTOK;
  654.             dem->msgCode = dexNotAFax;
  655.             break;
  656.         
  657.         case dexDisconnect:
  658.             /* fatal message */
  659.             sprintf(dem->msgBuf, "Lost connection to fax machine");
  660.             PP_TRACE(("%s", dem->msgBuf));
  661.             dem->msgCode = dexDisconnect;
  662.             retcode = NOTOK;
  663.             break;
  664.  
  665.         case dexNotReady:
  666.             /* msgBuf already set by previous response */
  667.             PP_TRACE(("%s", dem->msgBuf));
  668.             retcode = NOTOK;
  669.             break;
  670.         
  671.         case dexReady:
  672.             PP_TRACE(("Call terminated normally"));
  673.             break;
  674.         
  675.         default:
  676.             PP_TRACE(("%s", codeToString(code)));
  677.     }
  678.     return(retcode);
  679. }
  680.  
  681. /*
  682.  *    Send file in mode specified (g3 or ascii). Return OK if sent,
  683.  *    NOTOK if not sent. 
  684.  */
  685. sendDexModem(dem, data, dataLen, textMode)
  686. DexModem        *dem;
  687. unsigned char    *data;    /* data to send */
  688. int                dataLen;    /* of buf */
  689. int        textMode;    /* true if text, false if image */
  690. {
  691.     char    *mesg;
  692.     int        retcode = OK;
  693.     int        blockSize = 512;
  694.  
  695.     PP_TRACE(("Enter sendDexModem"));
  696.  
  697.     dem->msgBuf[0] = (char)0;
  698.     dem->errBuf[0] = (char)0;
  699.  
  700.     /* turn on image if g3 and not running at 19.2 */
  701.     if ((!textMode) && (!dem->highSpeed)) {
  702.         IssueCommand(dem, IMAGE_MODE, "Set Image Mode");
  703.     }
  704.  
  705. /* TODO: what if this blocks */
  706.     nonBlockIO(dem->fd);
  707.  
  708.     while (dataLen > 0) {
  709.         int                bytesToWrite;
  710.         unsigned char    *buf;
  711.  
  712.         buf = data;
  713.         bytesToWrite = dataLen > blockSize ? blockSize : dataLen;
  714.         data += bytesToWrite;
  715.         dataLen -= bytesToWrite;
  716.     
  717.         while (1) {
  718.             int    cc;
  719.             cc = write(dem->fd, buf, bytesToWrite);
  720.             if (cc == bytesToWrite)
  721.                 break;
  722.             else if ((cc == -1) && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) {
  723.                 fd_set    readfds, writefds;
  724.  
  725.                 FD_ZERO(&readfds);
  726.                 FD_ZERO(&writefds);
  727.                 FD_SET(dem->fd, &readfds);
  728.                 FD_SET(dem->fd, &writefds);
  729.  
  730.                 if (select(dem->fd+1, &readfds, &writefds, NULL, NULL) < 0) {
  731.                     sprintf(dem->errBuf, "select: %s", sys_errname(errno));
  732.                     retcode = NOTOK;
  733.                     goto quit;
  734.                 }
  735.                 if (FD_ISSET(dem->fd, &readfds)) {
  736.                     int    code;
  737.  
  738.                     code = readResponse(dem, &mesg);
  739.                     retcode = processResponse(dem, code, mesg);
  740.                     if (retcode == NOTOK) goto quit;
  741.                 }
  742.             } else if (cc > 0) {
  743.                 bytesToWrite -= cc;
  744.                 buf += cc;
  745.             } else {
  746.                 sprintf(dem->errBuf, "write: %s", 
  747.                     sys_errname(errno));
  748.                 retcode = NOTOK;
  749.                 goto quit;
  750.             }
  751.         }
  752.     }
  753.  
  754. quit:
  755.     PP_TRACE(("Leave sendDexModem, ret %d", retcode));
  756.     return(retcode);
  757. }
  758.  
  759. eomDexModem(dem)
  760. DexModem    *dem;
  761. {
  762.     int        retcode = OK;
  763.     int        code;
  764.  
  765.     blockIO(dem->fd);
  766.  
  767.     if (writeDexModem(dem, END_OF_TRANS, sizeof(END_OF_TRANS)-1, 
  768.         "send EOT") == NOTOK) {
  769.         sprintf(dem->msgBuf, "Error communicating with local modem");
  770.         return(NOTOK);
  771.     }
  772.  
  773.     do {
  774.         char    *mesg = NULL;
  775.         code = readResponse(dem, &mesg);
  776.         retcode = processResponse(dem, code, mesg);
  777.     } while (!((retcode == NOTOK) || (code == dexReady)));
  778.  
  779.     return(retcode);
  780. }
  781.  
  782. closeDexModem(dem)
  783. DexModem    *dem;
  784. {
  785.     if (dem->highSpeed)
  786.         IssueCommand(dem, BAUD_9600, "Set baud 9600");
  787.     close(dem->fd);
  788.     return(OK);
  789. }
  790.